/*******************************************************************************
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Jean Neveux - initial API and implementation
 *******************************************************************************/
package org.dynamo.database.reverse.config.internal;

import java.util.HashMap;
import java.util.Map;

import org.dynamo.database.reverse.config.internal.log.Log;
import org.dynamo.database.reverse.config.strings.internal.StringDbsUtils;
import org.dynamo.database.ui.console.DTULogger;
import org.eclipse.core.resources.IFile;
import org.eclipse.datatools.connectivity.sqm.core.rte.jdbc.JDBCColumn;
import org.eclipse.datatools.connectivity.sqm.core.rte.jdbc.JDBCForeignKey;
import org.eclipse.datatools.connectivity.sqm.core.rte.jdbc.JDBCTable;
import org.eclipse.datatools.modelbase.sql.constraints.UniqueConstraint;
import org.eclipse.datatools.modelbase.sql.schema.Schema;
import org.eclipse.datatools.modelbase.sql.tables.Column;
import org.eclipse.datatools.modelbase.sql.tables.Table;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;

public class SQLDTPConverter {

	private static final String IDENTIFIER_STEREOTYPE = "Identifier";
	private static final String ENTITY_STEREOTYPE = "Entity";
	public static final Log log = Log.getLog(SQLDTPConverter.class);
	private UMLUtilities umlUtilities;
	private IFile configfile;

	public SQLDTPConverter() {
		DTULogger.initialize();
	}

	public void perform(final String modelPath, final Schema schema) {
		
		umlUtilities = new UMLUtilities(configfile);

		try {
			String catalog = "default";
			if(schema.getCatalog() != null) {
				catalog = schema.getCatalog()
						.getName();
			}
			
			DTULogger.info("***************************************************");
			DTULogger.info("Starting conversion for :"+schema.getDatabase().getName()+
					"/"+catalog+"/"+schema.getName());
			
			Model model = umlUtilities.createUmlModel(catalog);

			Package package_ = umlUtilities.createPackage(model, schema
					.getName());

			Map<String, Class> classContainer = new HashMap<String, Class>(10);
			Map<String, Table> tableContainer = new HashMap<String, Table>(10);

			Stereotype stereotypeEntity = umlUtilities
					.getStereotype(ENTITY_STEREOTYPE);// umlUtilities.getDatabaseStereotype(ENTITY_STEREOTYPE);
			Stereotype stereotypeIdentifier = umlUtilities
					.getStereotype(IDENTIFIER_STEREOTYPE);

			for (Object tableObject : schema.getTables()) {
				Table table = (Table) tableObject;
				if (!isFKTable(table)) {
					DTULogger.info("Converting table: "+table.getName()+" : "+
							StringDbsUtils.formatTableName(table
									.getName()));
					tableContainer.put(StringDbsUtils.formatTableName(table
							.getName()), table);
					Class class_ = umlUtilities.createClass(StringDbsUtils
							.formatTableName(table.getName()), package_);

					if (stereotypeEntity != null) {
						try {
							class_.applyStereotype(stereotypeEntity);
						} catch (Exception e) {
							DTULogger
									.error("Could not apply stereotype on class name:"
											+ class_.getName());
						}
					}

					for (Object columObject : table.getColumns()) {
						Column col = (Column) columObject;
						if (col != null & col.getDataType() != null) {
							String strType = col.getDataType().getName();
							if (!col.isPartOfForeignKey()) {
								Property prop = umlUtilities
										.createSimpleProperty(
												StringDbsUtils
														.formatColumnName(col
																.getName()),
												umlUtilities.getType(strType),
												class_);
								DTULogger.info("Adding col -->"+col.getDataType().getName()
										+" : "+StringDbsUtils
										.formatColumnName(col
												.getName()));
								if (col.isPartOfPrimaryKey()) {
									if (stereotypeIdentifier != null) {
										try {
											prop
													.applyStereotype(stereotypeIdentifier);
										} catch (Exception e) {
											log
													.error("Could not apply stereotype on property name:"
															+ prop.getName());
										}
									}
								}
							}
						}
					}

					classContainer.put(class_.getName(), class_);
				}
			}
			getFkeys(schema, package_, classContainer);

			umlUtilities.save(modelPath, model);
			DTULogger.info("Generated UML file platform:/"+modelPath);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void getFkeys(Schema schema, Package package_,
			Map<String, Class> classContainer) {
		for (Object tableObject : schema.getTables()) {
			if (tableObject instanceof JDBCTable) {
				JDBCTable sourceTable = (JDBCTable) tableObject;
				if (isFKTable(sourceTable)) {
					if (sourceTable.getForeignKeys().size() == 2) {
						int startLower = 0;
						int endLower = 0;
						JDBCForeignKey sourceForeignKey = (JDBCForeignKey) sourceTable
								.getForeignKeys().get(0);
						JDBCForeignKey targetForeignKey = (JDBCForeignKey) sourceTable
								.getForeignKeys().get(1);
						JDBCTable srcTbl = (JDBCTable) sourceForeignKey
								.getUniqueConstraint().getBaseTable();
						JDBCTable trgTbl = (JDBCTable) targetForeignKey
								.getUniqueConstraint().getBaseTable();

						DTULogger
								.debug("========================== FOREIGN KEY TABLE =============================");
						DTULogger.debug("Source table:" + srcTbl.getName());

						for (Object memberObject : sourceForeignKey
								.getMembers()) {
							JDBCColumn col = (JDBCColumn) memberObject;
							if (!col.isNullable())
								startLower = 1;
							DTULogger.debug("Source column name:" + col.getName()
									+ " nullable:" + col.isNullable());
						}
						DTULogger.debug("Target table:" + trgTbl.getName());
						for (Object memberObject : targetForeignKey
								.getMembers()) {
							JDBCColumn col = (JDBCColumn) memberObject;
							if (!col.isNullable())
								endLower = 1;
							DTULogger.debug("Target column name:" + col.getName()
									+ " nullable:" + col.isNullable());
						}

						Class refClass = classContainer.get(StringDbsUtils
								.formatTableName(srcTbl.getName()));
						Class destClass = classContainer.get(StringDbsUtils
								.formatTableName(trgTbl.getName()));
						umlUtilities.createAssociationMultiplicity(
								StringDbsUtils.formatForeignKey(sourceTable
										.getName(), true), refClass, -1,
								endLower, destClass, -1, startLower, package_);
						DTULogger.info("Adding association --> src: "+
								StringDbsUtils
								.formatTableName(srcTbl.getName()) +
								" dest: "+StringDbsUtils
								.formatTableName(trgTbl.getName()));

					}
				} else {
					for (Object foreignKeyObject : sourceTable.getForeignKeys()) {
						int lower = 0;
						JDBCForeignKey foreignKey = (JDBCForeignKey) foreignKeyObject;
						UniqueConstraint uniqueConstraint = foreignKey
								.getUniqueConstraint();

						JDBCTable targetTable = (JDBCTable) foreignKey
								.getUniqueConstraint().getBaseTable();
						DTULogger
								.debug("==========================================================================");
						DTULogger.debug("Constraint name:" + foreignKey.getName());
						DTULogger.debug("Unique constraint:"
								+ uniqueConstraint.getName());
						DTULogger.debug("Source table:" + sourceTable.getName());
						DTULogger.debug("Target table:" + targetTable.getName());

						DTULogger
								.debug("------------------------------ Foreign keys MEMBERS ----------------------");
						for (Object memberObject : foreignKey.getMembers()) {
							JDBCColumn col = (JDBCColumn) memberObject;
							if (!col.isNullable())
								lower = 1;
							DTULogger.debug("Col name:" + col.getName()
									+ " nullable:" + col.isNullable());
						}
						DTULogger
								.debug("------------------------------ UNIQUE MEMBERS ----------------------------");
						for (Object memberObject : uniqueConstraint
								.getMembers()) {
							JDBCColumn col = (JDBCColumn) memberObject;
							DTULogger.debug("Col name:" + col.getName()
									+ " nullable:" + col.isNullable());
						}

						Class destClass = classContainer.get(StringDbsUtils
								.formatTableName(targetTable.getName()));
						Class refClass = classContainer.get(StringDbsUtils
								.formatTableName(sourceTable.getName()));
						umlUtilities.createAssociationMultiplicity(
								StringDbsUtils.formatForeignKey(targetTable
										.getName(), false), destClass, 1, 0,
								refClass, -1, lower, package_);
						DTULogger.info("Adding association --> src: "+
								StringDbsUtils
								.formatTableName(sourceTable.getName()) +
								" dest: "+StringDbsUtils
								.formatTableName(targetTable.getName()));
					}
				}
			}
		}
	}

	private boolean isFKTable(Table table) {
		boolean onlyFk = true;
		for (Object colObject : table.getColumns()) {
			Column col = (Column) colObject;
			if (!col.isPartOfForeignKey())
				onlyFk = false;
		}
		return onlyFk;
	}

	public void setConfigfile(IFile configfile) {
		this.configfile = configfile;
	}
}
